#include <cstdlib>
#include <QPoint>
#include <QImage>
#include <QPainter>
#include <QFile>
#include <boost/thread.hpp>
#include "ImageHandler.h"


class ImageHandler;
class ImageHandlerThread;



//CTOR
ImageHandler::ImageHandler(QFile* files, int numFiles)
{
	//copy references
	unloadedFiles=files;
	num_files=numFiles;
	
	finalImage=NULL;
	tempImages=NULL;
}

ImageHandler::~ImageHandler()
{
	//TODO: STOP THE IMAGE HANDLING THREAD BEFORE DELETING THE OBJECT
	//interrupt() or join() the thread?
	handlerThread->interrupt();
	delete handlerThread;
	
	delete finalImage;
}


/**
 * NOTE:  THIS FUNCTION RETURNS AN ARRAY OF ALLOCATED MEMORY,
 * DO NOT FORGET TO DELETE IT
 */
QImage* ImageHandler::loadAllImages(QFile* files, int num_files)
{
	//loop through the passed files to
	//check if each exists and is not null
	int num_valid_files=0;
	for(int i = 0; i < num_files; i++)
	{
		if(files[i].exists())
			num_valid_files++;
	}
	
	//if there are no valid files,
	//return null
	if(num_valid_files == 0)
		return NULL;
	
	//create an array of QImages as large as the number
	//of valid files
	QImage* images = new QImage[num_valid_files];
	
	num_images=0;
	
	/*
	 * loop through every file
	 * check if its valid
	 * if its valid,
	 * load the QImage from that file
	 * if an error occurs while loading,
	 * call the function registered
	 * with registerOnErrorCallback
	 */
	for(int i = 0; i < num_files; i++)
	{
		if(files[i].exists())
		{
			if(images[i].load(&files[i], (const char*)0))
					(*onErrorMethod)();
			
			num_images++;
		}
	}
	
	//loop through and check to make sure at least
	//one file is null
	bool atLeastOneValidFile = false;
	for(int i = 0; i < num_valid_files; i++)
	{
		if(!images[i].isNull())
			atLeastOneValidFile=true;
	}
	//if there are no valid files, return
	//NULL
	if(atLeastOneValidFile == false)
		return NULL;
	
	//return the array of loaded images
	return images;
}

/**
 * NOTE:  THIS FUNCTION RETURNS A DYNAMICALLY ALLOCATED QIMAGE
 * DO NOT FORGET TO DELETE IT
 */
QImage* ImageHandler::padImage(QImage* sourceImage, int padding)
{
	//if the passed image is null, return null
	if(sourceImage == NULL)
		return NULL;
	
	
	int newImageHeight = sourceImage->height() + (padding*2);
	int newImageWidth  = sourceImage->width()  + (padding*2);
	
	QImage*   newImage = new QImage(newImageWidth, newImageHeight, sourceImage->format());
	
	QPainter* painter = new QPainter(newImage);
	
	QPoint point(padding, padding);	
	painter->drawImage(point, *sourceImage);
	painter->end();
	
	delete painter;
	
	return newImage;
}

void ImageHandler::registerOnCompletionCallback(
		void(*callback)(QImage*))
{
	onCompletionMethod = callback;
}

void ImageHandler::registerOnErrorCallback(void(*callback)(void))
{
	onErrorMethod = callback;
}

void ImageHandler::asyncHandleImages()
{
	//the object of ImageHandlerThread is copied into internal memory
	//so there's no reason to maintain a copy of it
	handlerThread =  new boost::thread(ImageHandlerThread(this));
}

void ImageHandler::callOnCompletionMethod(QImage* image)
{
	onCompletionMethod(image);
}

void ImageHandler::callOnErrorCallback()
{
	onErrorMethod();
}

void ImageHandler::setFinalImage(QImage* image)
{
	finalImage = image;
}

QImage* ImageHandler::assembleImages(QImage* images, int num_images)
{
	
}


QFile* ImageHandler::getFiles()
{
	return unloadedFiles;
}

int ImageHandler::getNumFiles()
{
	return num_files;
}

QImage* ImageHandler::getTempImages()
{
	return tempImages;
}

int ImageHandler::getNumImages()
{
	return num_images;
}

//-------------------------------------
//IMAGEHANDLERTHREAD IMPLEMENTATIONS
//-------------------------------------

ImageHandlerThread::ImageHandlerThread(ImageHandler* HANDLER)
{
	handler=HANDLER;
}

ImageHandlerThread::~ImageHandlerThread()
{
	
}

/**
 * This method is called by the boost::thread and will run
 * asynchronously of the main thread
 */
void ImageHandlerThread::operator ()()
{
	try {
	unprocessedImages = handler->loadAllImages(handler->getFiles(),
								handler->getNumFiles());
	
	if(unprocessedImages == NULL)
		throw new std::exception();
	
	QImage *paddedImages = new QImage[num_images];

	//pad all images
	for(int i = 0; i < num_images; i++)
	{
		paddedImages[i] = *handler->padImage(&unprocessedImages[i], 3);
	}

	//deallocate the unpadded images
	delete[] unprocessedImages;

	QImage* finalImage = handler->assembleImages(paddedImages, num_images);

	if(finalImage == NULL)
		throw new std::exception();
	
	handler->setFinalImage(finalImage);
	
	handler->callOnCompletionMethod(finalImage);
	
	delete[] unprocessedImages;
	}
	catch(std::exception e)
	{
		handler->callOnErrorCallback();
	}
}

//-------------------------------------
//END IMAGEHANDLERTHREAD IMPLEMENTATIONS
//-------------------------------------


//-------------------------------------
//TEXTUREALLOCATOR IMPLEMENTATIONS
//-------------------------------------


TextureAllocator::TextureAllocator(QImage* images,
			int width=1024, int height=1024)
{
	if(width != height)
		//TODO: HANDLE ERROR
		
	if(!checkPowerOfTwo(width) || !checkPowerOfTwo(height))
	{
		//TODO: HANDLE ERROR
	}
	
	//Divide the image into chunks
}




//-------------------------------------
//END TEXTUREALLOCATOR IMPLEMENTATIONS
//-------------------------------------

